package com.zone.mybatis.builder.xml;

import cn.hutool.core.util.StrUtil;
import com.zone.mybatis.datasource.DataSourceFactory;
import com.zone.mybatis.io.Resources;
import com.zone.mybatis.mapping.BoundSql;
import com.zone.mybatis.mapping.Environment;
import com.zone.mybatis.mapping.MappedStatement;
import com.zone.mybatis.mapping.SqlCommandType;
import com.zone.mybatis.session.Configuration;
import com.zone.mybatis.transaction.TransactionFactory;
import com.zone.mybatis.type.TypeAliasRegistry;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author: zongzi
 * @description: TODO
 * @date: 2023/12/17
 */
public class XmlConfigBuild {

    private final Configuration configuration;

    public XmlConfigBuild() {
        configuration = new Configuration();
    }


    public Configuration parse(InputStream inputStream) {
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(inputStream);
            this.parseConfiguration(document.getRootElement());
            return configuration;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void parseConfiguration(Element root) throws Exception {
        parseMappers(root.element("mappers"));
        environmentsElement(root.element("environments"));
    }

    private void parseMappers(Element mappers) throws Exception {
        for (Element mapper : mappers.elements()) {
            String resource = mapper.attributeValue("resource");
            try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
                this.parseMapper(inputStream);
            }
        }
    }

    private void parseMapper(InputStream inputStream) throws Exception {
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read(inputStream);
        Element mapper = document.getRootElement();
        String namespace = mapper.attributeValue("namespace");

        List<Element> elements = mapper.elements();
        for (Element element : elements) {
            if (SqlCommandType.SELECT.name().equals(element.getName().toUpperCase(Locale.ENGLISH))) {
                String id = element.attributeValue("id");
                String msId = String.format("%s.%s", namespace, id);
                String parameterType = element.attributeValue("parameterType");
                String resultType = element.attributeValue("resultType");
                SqlCommandType sqlCommandType = SqlCommandType.SELECT;
                String sql = element.getText();
                Map<Integer, String> parameterMappings = new HashMap<>();


                Pattern pattern = Pattern.compile("(#\\{(.*?)})");
                Matcher matcher = pattern.matcher(sql);
                for (int i = 1; matcher.find(); i++) {
                    // #{id}
                    String firstGroup = matcher.group(1);
                    // id
                    String secondGroup = matcher.group(2);
                    parameterMappings.put(i, secondGroup);
                    sql = sql.replace(firstGroup, "?");
                }
                BoundSql boundSql = new BoundSql(parameterType, parameterMappings, resultType, sql);

                MappedStatement mappedStatement = new MappedStatement(namespace, id, msId, sqlCommandType, boundSql);

                configuration.addMappedStatement(mappedStatement);
            }
        }
        configuration.addMapper(Class.forName(namespace));
    }

    private void environmentsElement(Element environments) throws Exception {
        String defaultValue = environments.attributeValue("default");
        if (StrUtil.isEmpty(defaultValue)) {
            return;
        }
        TypeAliasRegistry typeAliasRegistry = configuration.getTypeAliasRegistry();
        List<Element> environmentList = environments.elements();
        for (Element environment : environmentList) {
            String id = environment.attributeValue("id");
            if (!defaultValue.equals(id)) {
                continue;
            }
            TransactionFactory transactionFactory = typeAliasRegistry.<TransactionFactory>getType(environment.element("transactionManager").attributeValue("type")).newInstance();
            Element dataSourceElement = environment.element("dataSource");
            DataSourceFactory dataSourceFactory = typeAliasRegistry.<DataSourceFactory>getType(dataSourceElement.attributeValue("type")).newInstance();
            List<Element> propertyElementList = dataSourceElement.elements();
            Properties properties = new Properties();
            for (Element property : propertyElementList) {
                properties.setProperty(property.attributeValue("name"), property.attributeValue("value"));
            }
            dataSourceFactory.setProperties(properties);
            Environment env = new Environment(id, transactionFactory, dataSourceFactory.getDataSource());
            configuration.setEnvironment(env);
        }
    }

    public static void main(String[] args) {
        String sql = "        SELECT id, userId, userHead, createTime\n" +
                "        FROM user\n" +
                "        where id = #{id} and name = #{name}";
        Pattern pattern = Pattern.compile("(#\\{(.*?)})");
        Matcher matcher = pattern.matcher(sql);
        for (int i = 1; matcher.find(); i++) {
            // #{id}
            String firstGroup = matcher.group(1);
            System.out.println(firstGroup);
            // id
            String secondGroup = matcher.group(2);
            System.out.println(secondGroup);
        }


    }
}
